using System;
using System.Collections.Generic;
using Core.Utils;
using UnityEngine;
using UnityEngine.InputSystem.LowLevel;
using Util;

namespace Core {
    public class Timer : IResetable {
        public uint Id;
        public float Elapse;
        public float Delay;
        public int Times;
        public float Interval;
        public Action OnExecute;
        public Action OnKill;
        public bool MarkAsKill;
        public bool IsExecuted;
        public void Reset() {
            Id = 0;
            MarkAsKill = false;
            IsExecuted = false;
            Elapse = .0f;
            Delay = .0f;
            Interval = .0f;
            Times = 0;
            OnExecute = null;
        }

        public bool Update(float deltaTime) {
            if (MarkAsKill) {
                return true;
            }
            Elapse += deltaTime;
            if (!IsExecuted) {
                if (Elapse >= Delay) {
                    Elapse -= Delay;
                    Times--;
                    OnExecute?.Invoke();
                    IsExecuted = true;
                }
            }

            if (IsExecuted) {
                while (Elapse >= Interval && Times > 0) {
                    Elapse -= Interval;
                    Times--;
                    OnExecute?.Invoke();
                }
            }
            return Times <= 0;
        }
    }
    
    public class TimerMgr : MonoSingleton<TimerMgr> {

        private static uint sId = 1;
        private List<Timer> mAddList = new();
        private List<Timer> mUpdateList = new();
        private List<Timer> mRemoveList = new();

        private Dictionary<uint, Timer> mTimerDic = new();

        private void Update() {
            for (int i = 0;i < mAddList.Count;++i){
                mUpdateList.Add(mAddList[i]);
            }
            
            
            for (int i = 0; i < mUpdateList.Count; ++i) {
                if (mUpdateList[i].MarkAsKill) {
                    continue;
                }

                if (mUpdateList[i].Update(Time.deltaTime)) {
                    mUpdateList[i].OnKill?.Invoke();
                    mRemoveList.Add(mUpdateList[i]);
                }
            }

            for (int i = 0; i < mRemoveList.Count; ++i) {
                mTimerDic.Remove(mRemoveList[i].Id);
                mUpdateList.Remove(mRemoveList[i]);
                PoolMgr.Ins.Release(mRemoveList[i]);
            }
            
            mAddList.Clear();
            mRemoveList.Clear();
        }

        private Timer registerTimer(float delay, Action onExecute, int times, float interval,Action onKill) {
            Timer timer = PoolMgr.Ins.Get<Timer>();
            timer.Delay = delay;
            timer.OnExecute = onExecute;
            timer.OnKill = onKill;
            timer.Times = times;
            timer.Interval = interval;
            timer.Id = sId++;
            mTimerDic.Add(timer.Id,timer);
            mAddList.Add(timer);
            return timer;
        }
        
        public uint AddOnceTask(float delay,Action onExecute,Action onKill = null) {
            return registerTimer(delay,onExecute,1,0,onKill).Id;
        }
        public uint AddTask(float delay,Action onExecute,int times,float interval,Action onKill = null) {
            return registerTimer(delay,onExecute,times,interval,onKill).Id;
        }

        public void RemoveTask(uint id) {
            if (mTimerDic.TryGetValue(id, out Timer timer)) {
                timer.MarkAsKill = true;
                mRemoveList.Add(timer);
            }
        }
    }
}
